/**
 * gnome-gmail-notifier: the gnome gmail notifier.
 * Copyright (C) 2007 Bradley A. Worley.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * 
 *     Free Software Foundation, Inc.
 *     59 Temple Place, Suite 330
 *     Boston, MA 02111-1307 USA
 **/

/*
 * include our application header.
 */
#include <main.h>

/*
 * private object definition.
 */
struct _GgnManagerPrivate {
    /* the preferences file. */
    GgnPreferences* prefs;
    
    /* the presence controls. */
    GgnPresence* presence;
    
    /* the about dialog. */
    GgnAboutDialog* about;
    
    /* the preferences window. */
    GgnConfig* config;
    
    /* the atom feeds. */
    GgnAtomFeed* cur;
    GPtrArray* feeds;
    gboolean checking;
};

/*
 * forward function definitions.
 */
static void ggn_manager_init (GgnManager* self);
static void ggn_manager_class_init (GgnManagerClass* klass);
static void ggn_manager_finalize (GObject* obj);

/*
 * define the gobject type and its basic functions.
 */
G_DEFINE_TYPE (GgnManager, ggn_manager, G_TYPE_OBJECT);

/*
 * ggn_manager_handle_inbox_change:
 *
 * Called by the ggn_manager_feed_checked function when
 * that function detects that the inbox has been modified
 * in some way and the interface needs updating.
 *
 * Return value: void.
 */
void ggn_manager_handle_inbox_change (GgnManager* manager,
                                      gint messages,
                                      gint inboxes,
                                      gint errors) {
    /* lock the gtk thread. */
    gdk_threads_enter ();
    
    /* define helping variables. */
    static gint last_messages, last_errors;
    gchar* tip = NULL;
    gint num = 0, used = 0;
    gboolean react = messages > last_messages || errors != last_errors ? TRUE : FALSE;
    
    /* see if we have errors or messages. */
    if (errors > 0) {
        /* set the tooltip. */
        tip = g_strdup (_("Unable to check mail"));
        
        /* set the icon style. */
        ggn_presence_set_icon_style (manager->priv->presence,
                                     GGN_PRESENCE_STYLE_ERROR);
        
        /* set the icon tooltip. */
        ggn_presence_set_icon_tip (manager->priv->presence, tip);
        
        /* see if we notify the user. */
        if ((ggn_preferences_get_notify_err (manager->priv->prefs)) &&
            (react == TRUE)) {
            /* create notification strings. */
            GString* ttl = g_string_new ("");
            GString* sum = g_string_new ("");
            
            /* start the summary string. */
            g_string_printf (sum,
                             _("Errors occurred while checking mail for "));
            
            /* set the title string. */
            g_string_printf (ttl, _("Unable to check mail"));
            
            /* loop for each account. */
            for (num = 0; num < manager->priv->feeds->len; num++) {
                /* get a reference to a feed. */
                manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                        (manager->priv->feeds,
                                                         num);
                
                /* see if the account has an error. */
                if ((ggn_atom_feed_get_enabled (manager->priv->cur)) &&
                    (ggn_atom_feed_get_error (manager->priv->cur))) {
                    /* set variables we'll use. */
                    gchar* name = ggn_atom_feed_get_name (manager->priv->cur);
                    used++;
                    
                    /* grammar can be tricky. */
                    if (errors == 1) {
                        /* only account of one. */
                        g_string_append_printf (sum, "%s ", name);
                    }
                    else {
                        /* more than one account. */
                        if (used == errors) {
                            /* last account. */
                            g_string_append_printf (sum, "%s %s",
                                                    _("and"),
                                                    name);
                        }
                        else if (used == errors - 1) {
                            /* next to last account. */
                            g_string_append_printf (sum, "%s ", name);
                        }
                        else {
                            /* any other account. */
                            g_string_append_printf (sum, "%s, ", name);
                        }
                    }
                    
                    /* free the string we used. */
                    g_free (name);
                }
            }
            
            /* set the notification style. */
            ggn_presence_set_notify_style (manager->priv->presence,
                                           GGN_PRESENCE_STYLE_ERROR);
            
            /* set the notification text. */
            ggn_presence_set_notify_title (manager->priv->presence, ttl->str);
            ggn_presence_set_notify_summary (manager->priv->presence, sum->str);
            
            /* show the notification. */
            ggn_presence_show_notify (manager->priv->presence);
            
            /* free the strings created. */
            g_string_free (ttl, FALSE);
            g_string_free (sum, FALSE);
        }
    }
    else if (messages > 0) {
        /* declare a flexible string. */
        GString* mtip = g_string_new ("");
        guint counted = 0;
        
        /* loop for each user to get the messages. */
        for (num = 0; num < manager->priv->feeds->len; num++) {
            /* get a reference to a feed. */
            manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                    (manager->priv->feeds,
                                                     num);
            
            /* see if this feed contains new messages. */
            if ((ggn_atom_feed_get_enabled (manager->priv->cur)) &&
                (ggn_atom_feed_get_count (manager->priv->cur) > 0)) {
                
                /* see how many messages we have. */
                if (ggn_atom_feed_get_count (manager->priv->cur) == 1) {
                    /* singular noun. */
                    g_string_append_printf (mtip, _("%d new message for %s"),
                                            ggn_atom_feed_get_count (manager->priv->cur),
                                            ggn_atom_feed_get_name (manager->priv->cur));
                }
                else {
                    /* plural noun. */
                    g_string_append_printf (mtip, _("%d new messages for %s"),
                                            ggn_atom_feed_get_count (manager->priv->cur),
                                            ggn_atom_feed_get_name (manager->priv->cur));
                }
                
                /* each user gets a line of the tip text. */
                counted++;
                if (counted < inboxes) {
                    /* add a newline. */
                    g_string_append (mtip, "\n");
                }
            }
        }
        
        /* set the tooltip string. */
        tip = g_strdup (mtip->str);
        g_string_free (mtip, TRUE);
        
        /* set the icon style. */
        ggn_presence_set_icon_style (manager->priv->presence,
                                     GGN_PRESENCE_STYLE_MESSAGE);
        
        /* set the icon tooltip. */
        ggn_presence_set_icon_tip (manager->priv->presence, tip);
        
        /* see if we notify the user. */
        if ((ggn_preferences_get_notify_new (manager->priv->prefs)) &&
            (react == TRUE)) {
            /* create notification strings. */
            GString* ttl = g_string_new ("");
            GString* sum = g_string_new ("");
            
            /* make sure we use proper grammar. */
            if (messages == 1) {
                /* singular noun. */
                g_string_printf (sum,
                                 _("There is <b>%d</b> new message for "),
                                 messages);
            }
            else {
                /* plural noun. */
                g_string_printf (sum,
                                 _("There are <b>%d</b> new messages for "),
                                 messages);
            }
            
            /* set the title string. */
            g_string_printf (ttl, _("You have new mail"));
            
            /* loop for each account. */
            for (num = 0; num < manager->priv->feeds->len; num++) {
                /* get a reference to a feed. */
                manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                        (manager->priv->feeds,
                                                         num);
                
                /* see if this feed contains new messages. */
                if ((ggn_atom_feed_get_enabled (manager->priv->cur)) &&
                    (ggn_atom_feed_get_count (manager->priv->cur) > 0)) {
                    /* set variables we'll use. */
                    gchar* name = ggn_atom_feed_get_name (manager->priv->cur);
                    used++;
                    
                    /* grammar can be tricky. */
                    if (inboxes == 1) {
                        /* only account of one. */
                        g_string_append_printf (sum, "%s ", name);
                    }
                    else {
                        /* more than one account. */
                        if (used == inboxes) {
                            /* last account. */
                            g_string_append_printf (sum, "%s %s",
                                                    _("and"),
                                                    name);
                        }
                        else if (used == inboxes - 1) {
                            /* next to last account. */
                            g_string_append_printf (sum, "%s ", name);
                        }
                        else {
                            /* any other account. */
                            g_string_append_printf (sum, "%s, ", name);
                        }
                    }
                    
                    /* free the string we used. */
                    g_free (name);
                }
            }
            
            /* set the notification style. */
            ggn_presence_set_notify_style (manager->priv->presence,
                                           GGN_PRESENCE_STYLE_MESSAGE);
            
            /* set the notification text. */
            ggn_presence_set_notify_title (manager->priv->presence, ttl->str);
            ggn_presence_set_notify_summary (manager->priv->presence, sum->str);
            
            /* show the notification. */
            ggn_presence_show_notify (manager->priv->presence);
            
            /* free the strings created. */
            g_string_free (ttl, FALSE);
            g_string_free (sum, FALSE);
        }
        
        /* see if we notify the user with sounds. */
        if ((ggn_preferences_get_sound_enab (manager->priv->prefs)) &&
            (react == TRUE)) {
            /* get the soundfile. */
            gchar* file = ggn_preferences_get_sound_file (manager->priv->prefs);
            
            /* play the sound. */
            ggn_presence_play_sound (manager->priv->presence, file);
            
            /* free the soundfile string. */
            g_free (file);
        }
    }
    else {
        /* set the icon style. */
        ggn_presence_set_icon_style (manager->priv->presence,
                                     GGN_PRESENCE_STYLE_NORMAL);
        
        /* set the icon tooltip. */
        tip = g_strdup (_("No new messages"));
        ggn_presence_set_icon_tip (manager->priv->presence, tip);
    }
    
    /* set the last variable. */
    last_messages = messages;
    last_errors = errors;
    
    /* free the tooltip string. */
    g_free (tip);
    
    /* we are no longer checking. */
    manager->priv->checking = FALSE;
    
    /* flush the pending commands to gtk. */
    gdk_flush ();
    
    /* unlock the gtk thread. */
    gdk_threads_leave ();
}

/*
 * ggn_manager_feed_checked:
 *
 * Called when an atom feed in our array finishes its
 * refresh.
 *
 * Return value: void.
 */
void ggn_manager_feed_checked (GgnAtomFeed* feed,
                               gboolean success,
                               gpointer data) {
    /* get a reference to our object. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* set the feed to checked. */
    ggn_atom_feed_set_checked (feed, TRUE);
    
    /* declare helping variables. */
    gint num = 0, msg_count = 0, inbox_count = 0, err_count = 0;
    gboolean finished = TRUE;
    
    /* loop for all of the feeds. */
    for (num = 0; num < manager->priv->feeds->len; num++) {
        /* get the current feed. */
        manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                (manager->priv->feeds, num);
        
        /* if this isn't finished, then say so. */
        if (!ggn_atom_feed_get_checked (manager->priv->cur)) {
            /* set our variable. */
            finished = FALSE;
        }
        else {
            /* see if its enabled. */
            if (ggn_atom_feed_get_enabled (manager->priv->cur)) {
                /* add to the message count. */
                msg_count += ggn_atom_feed_get_count (manager->priv->cur);
                
                /* add to the inbox count. */
                if (ggn_atom_feed_get_count (manager->priv->cur) > 0) {
                    /* one more inbox has messages. */
                    inbox_count++;
                }
                
                /* add to the error count. */
                if (ggn_atom_feed_get_error (manager->priv->cur)) {
                    /* one more inbox failed to check. */
                    err_count++;
                }
            }
        }
    }
    
    /* see if we've checked them all. */
    if (finished == TRUE) {
        /* send word to our interface update function. */
        ggn_manager_handle_inbox_change (manager,
                                         msg_count,
                                         inbox_count,
                                         err_count);
    }
}

/*
 * ggn_manager_update_array:
 *
 * Sets up the Gmail ATOM feeds using the information provided
 * by the preferences file. The manager object keeps a pointer
 * array of all of the atom feeds, so this function keeps the
 * array in sync with the preferences xml file.
 *
 * Return value: void.
 */
void ggn_manager_update_array (GgnManager* manager) {
    /* declare helping variables. */
    GgnAccount* account;
    GSList* accounts;
    gchar* email;
    gint num;

    /* cancel the update if we're checking mail. */
    if (manager->priv->checking)
        return;
    
    /* loop for each current atom feed. */
    for (num = 0; num < manager->priv->feeds->len; num++) {
        /* get a reference to the feed. */
        manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                (manager->priv->feeds, num);
        
        /* free and remove the feed. */
        ggn_atom_feed_free (manager->priv->cur);
    }
    
    /* clean out the pointer array. */
    if (manager->priv->feeds->len > 0) {
        /* this if statement avoids a warning. */
        g_ptr_array_remove_range (manager->priv->feeds, 0,
                                  manager->priv->feeds->len);
    }

    /* get the accounts. */
    accounts = ggn_preferences_get_accounts (manager->priv->prefs);

    /* see if we have no feeds. */
    if (accounts == NULL)
        return;
    
    /* loop for each account. */
    for (num = 0; num < g_slist_length (accounts); num++) {
        /* get the email. */
        email = g_slist_nth_data (accounts, num);

        /* get the account. */
        account = ggn_preferences_get_account (manager->priv->prefs, email);

        /* create a new atom feed. */
        manager->priv->cur = ggn_atom_feed_new ();
        
        /* set the feed name. */
        ggn_atom_feed_set_name (manager->priv->cur,
                                g_strdup (account->name));
        
        /* set the feed username. */
        ggn_atom_feed_set_user (manager->priv->cur,
                                g_strdup_printf ("%s@%s",
                                                 account->user,
                                                 account->domain));
        
        /* set the feed password. */
        ggn_atom_feed_set_pass (manager->priv->cur,
                                g_strdup (account->pass));
        
        /* set the feed enabled boolean. */
        ggn_atom_feed_set_enabled (manager->priv->cur, account->enab);
        
        /* set the feed checked boolean. */
        ggn_atom_feed_set_checked (manager->priv->cur, TRUE);
        
        /* set the feed proxy. */
        ggn_atom_feed_set_proxy (manager->priv->cur,
                                 ggn_preferences_get_proxy
                                     (manager->priv->prefs));
        
        /* set the proxy username */
        ggn_atom_feed_set_proxy_user (manager->priv->cur,
                                      ggn_preferences_get_proxy_user
                                         (manager->priv->prefs));

        /* set the proxy password. */
        ggn_atom_feed_set_proxy_pass (manager->priv->cur,
                                      ggn_preferences_get_proxy_pass
                                         (manager->priv->prefs));
        
        /* link it to our callback. */
        g_signal_connect (G_OBJECT (manager->priv->cur),
                          "updated",
                          G_CALLBACK (ggn_manager_feed_checked),
                          manager);
        
        /* add the new feed to the array. */
        g_ptr_array_add (manager->priv->feeds, manager->priv->cur);

        /* free the account and email string. */
        ggn_preferences_account_free (account);
        g_free (email);
    }

    /* free the listing. */
    g_slist_free (accounts);
}

/*
 * ggn_manager_refresh_feeds:
 *
 * Called as the main function which will update all of the atom
 * feeds connected to Gmail.
 *
 * Return value: void.
 */
void ggn_manager_refresh_feeds (GgnManager* manager) {
    /* update the feed array. */
    ggn_manager_update_array (manager);

    /* have we alerted before regarding zero feeds? */
    static gboolean warned_noaccounts;

    /* did we refresh any feeds? */
    guint refreshed = 0;
    
    /* don't check if we have no feeds to check. */
    if (manager->priv->feeds->len == 0) {
        /* print an annoying warning message. */
        if (warned_noaccounts == FALSE) {
            /* set our notification bubble style. */
            ggn_presence_set_notify_style (manager->priv->presence,
                                           GGN_PRESENCE_STYLE_WARNING);

            /* set our notification bubble's text. */
            ggn_presence_set_notify_title (manager->priv->presence,
                                           GGN_MANAGER_NOACCOUNTS_TITLE);
            ggn_presence_set_notify_summary (manager->priv->presence,
                                             GGN_MANAGER_NOACCOUNTS_SUMMARY);

            /* show the notification. */
            ggn_presence_show_notify (manager->priv->presence);

            /* don't show it more than once. */
            warned_noaccounts = TRUE;
        }

        /* don't check the mail. */
        return;
    }

    /* loop in the feeds array. */
    gint num;
    for (num = 0; num < manager->priv->feeds->len; num++) {
        /* get a reference to the feed. */
        manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                (manager->priv->feeds, num);
        
        /* if the feed is disabled, skip it. */
        if (ggn_atom_feed_get_enabled (manager->priv->cur) == TRUE) {
            /* here is a feed to check. */
            refreshed++;
        }
    }

    /* do we have feeds to refresh? */
    if (refreshed == 0) {
        /* no. don't try. */
        return;
    }
    
    /* set the tray icon style. */
    ggn_presence_set_icon_style (manager->priv->presence,
                                 GGN_PRESENCE_STYLE_CHECKING);
    
    /* set the tray icon tip. */
    ggn_presence_set_icon_tip (manager->priv->presence,
                               _("Checking mail..."));
    
    /* we are checking now. */
    manager->priv->checking = TRUE;
    
    /* loop again in the feeds array. */
    for (num = 0; num < manager->priv->feeds->len; num++) {
        /* get a reference to the feed. */
        manager->priv->cur = (GgnAtomFeed*) g_ptr_array_index
                                                (manager->priv->feeds, num);
        
        /* if the feed is disabled, skip it. */
        if (ggn_atom_feed_get_enabled (manager->priv->cur) == FALSE) {
            /* set it's checked value. */
            ggn_atom_feed_set_checked (manager->priv->cur, TRUE);
            continue;
        }
        
        /* tell the feed to update itself. */
        ggn_atom_feed_set_checked (manager->priv->cur, FALSE);
        ggn_atom_feed_update (manager->priv->cur);
    }
}

/*
 * ggn_manager_iteration:
 *
 * Called from a timeout loop that keeps the application checking
 * the feeds every so often. In a way, the loop re-instates itself
 * with the new timeout value.
 *
 * Return value: FALSE.
 */
gboolean ggn_manager_iteration (gpointer data) {
    /* get a reference to the manager. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* is this not our first run? */
    if (!ggn_preferences_get_firstrun (manager->priv->prefs)) {
        /* refresh the atom feeds. */
        ggn_manager_refresh_feeds (manager);
    }
    
    /* re-iterate ourself. */
    gint rate = ggn_preferences_get_rate (manager->priv->prefs);
    g_timeout_add (rate * 60 * 1000, ggn_manager_iteration, manager);
    
    /* exit the function. */
    return FALSE;
}

/*
 * ggn_manager_accounts_modified:
 *
 * Called when the preferences file thinks
 * we should update our atom feeds.
 *
 * Return value: void.
 */
static void ggn_manager_accounts_modified (GgnPreferences* prefs,
                                           gpointer data) {
    /* update the atom feed array. */
    GgnManager* manager = GGN_MANAGER (data);
    ggn_manager_update_array (manager);
}

/*
 * ggn_manager_icon_selected:
 *
 * This is called when the user left-clicks the
 * tray icon.
 *
 * Return value: void.
 */
static void ggn_manager_icon_selected (GgnPresence* pres,
                                       gpointer data) {
    /* get a reference to our object. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* is this the first run? */
    if (ggn_preferences_get_firstrun (manager->priv->prefs)) {
        /* get the filename of the about page. */
        gchar* file = g_strdup_printf ("file://%s/welcome.html",
                                       GLADEXML_DIR);
        
        /* now open the about page. */
        ggn_client_execute (file);
        
        /* free the filename string. */
        g_free (file);
        
        /* the first run only happens once. */
        ggn_preferences_set_firstrun (manager->priv->prefs, FALSE);
    }
    else {
        /* open the gmail inbox. */
        ggn_client_execute (GGN_ATOM_FEED_INBOX_URL);
    }
    
    /* exit the function. */
    return;
}

/*
 * ggn_manager_menu_check:
 *
 * This is called when the user selects the
 * "Check Mail" menu item from the tray
 * popup menu.
 *
 * Return value: void.
 */
static void ggn_manager_menu_check (GgnPresence* pres,
                                    gpointer data) {
    /* get a reference to our object. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* is this the first run? */
    if (!ggn_preferences_get_firstrun (manager->priv->prefs)) {
        /* refresh the atom feeds. */
        ggn_manager_refresh_feeds (manager);
    }
    
    /* exit the function. */
    return;
}

/*
 * ggn_manager_menu_prefs:
 *
 * This is called when the user selects the
 * "Preferences" menu item from the tray
 * popup menu.
 *
 * Return value: void.
 */
static void ggn_manager_menu_prefs (GgnPresence* pres,
                                    gpointer data) {
    /* get a reference to our object. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* show the preferences window. */
    ggn_config_show (manager->priv->config);
    
    /* exit the function. */
    return;
}

/*
 * ggn_manager_menu_about:
 *
 * This is called when the user selects the
 * "About" menu item from the tray popup
 * menu.
 *
 * Return value: void.
 */
static void ggn_manager_menu_about (GgnPresence* pres,
                                    gpointer data) {
    /* get a reference to our object. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* show the about dialog. */
    ggn_about_dialog_show (manager->priv->about);
    
    /* exit the function. */
    return;
}

/*
 * ggn_manager_menu_quit:
 *
 * This is called when the user selects the
 * "Quit" menu item from the tray popup menu.
 *
 * Return value: void.
 */
static void ggn_manager_menu_quit (GgnPresence* pres,
                                   gpointer data) {
    /* get a reference to our object. */
    GgnManager* manager = GGN_MANAGER (data);
    
    /* shutdown the manager. */
    ggn_manager_quit (manager);
    
    /* exit the function. */
    return;
}

/*
 * ggn_manager_init:
 *
 * This function is used by the gobject library to
 * generate a new instance of our object.
 */
static void ggn_manager_init (GgnManager* self) {
    /* set up the private data structure. */
    self->priv = g_new0 (GgnManagerPrivate, 1);
    
    /* setup private instance objects. */
}

/*
 * ggn_manager_class_init:
 *
 * This function is used by the gobject library to
 * generate a new class object of our object.
 */
static void ggn_manager_class_init (GgnManagerClass* klass) {
    /* setup a gobject class. */
    GObjectClass* gobj_class = G_OBJECT_CLASS (klass);
    
    /* set the locations of our destruction function. */
    gobj_class->finalize = ggn_manager_finalize;
    
    /* setup the default signal handlers. */
}

/*
 * ggn_manager_finalize:
 *
 * This function is used by the gobject library to cleanly finish
 * the destruction process started by the dispose function.
 */
static void ggn_manager_finalize (GObject* obj) {
    /* make a reference to ourself. */
    GgnManager* self = GGN_MANAGER (obj);
    
    /* free the private instance objects. */
    
    /* destroy the private object. */
    g_free (self->priv);
    self->priv = NULL;
    
    /* chain up to the parent class. */
    G_OBJECT_CLASS (ggn_manager_parent_class)->finalize (obj);
}

/*
 * ggn_manager_new:
 *
 * Creates a new GgnPresence object for use in the application.
 * It maintains a presence on the panel, with sound effects,
 * and with libnotify notifications.
 *
 * Return value: the new manager object.
 */
GgnManager* ggn_manager_new (void) {
    /* make a newly created gobject. */
    GgnManager* pres = g_object_new (GGN_TYPE_MANAGER, NULL);
    
    /* return the new object. */
    return pres;
}

/*
 * ggn_manager_free:
 *
 * Frees the given manager by decreasing its reference count.
 *
 * Return value: void.
 */
void ggn_manager_free (GgnManager* manager) {
    /* unreference the object. */
    while (G_IS_OBJECT (manager)) {
        /* unreference this object. */
        g_object_unref (G_OBJECT (manager));
    }
}

/*
 * ggn_manager_main:
 *
 * This function takes control from the application to setup
 * the application initially.
 *
 * Return value: void.
 */
void ggn_manager_main (GgnManager* manager, int argc, char** argv) {
    /* setup the preferences file. */
    manager->priv->prefs = ggn_preferences_new ();
    
    /* link to the accounts_modified signal. */
    g_signal_connect (G_OBJECT (manager->priv->prefs),
                      "accounts_modified",
                      G_CALLBACK (ggn_manager_accounts_modified),
                      manager);

    /* setup the presence object. */
    manager->priv->presence = ggn_presence_new ();
    
    /* set the icon style. */
    ggn_presence_set_icon_style (manager->priv->presence,
                                 GGN_PRESENCE_STYLE_NORMAL);
    
    /* show the status icon. */
    ggn_presence_set_icon_visible (manager->priv->presence, TRUE);
    
    /* link into the presence callback. */
    g_signal_connect (G_OBJECT (manager->priv->presence),
                      "selected",
                      G_CALLBACK (ggn_manager_icon_selected),
                      manager);
    
    /* link into the presence callback. */
    g_signal_connect (G_OBJECT (manager->priv->presence),
                      "check_clicked",
                      G_CALLBACK (ggn_manager_menu_check),
                      manager);
    
    /* link into the presence callback. */
    g_signal_connect (G_OBJECT (manager->priv->presence),
                      "prefs_clicked",
                      G_CALLBACK (ggn_manager_menu_prefs),
                      manager);
    
    /* link into the presence callback. */
    g_signal_connect (G_OBJECT (manager->priv->presence),
                      "about_clicked",
                      G_CALLBACK (ggn_manager_menu_about),
                      manager);
    
    /* link into the presence callback. */
    g_signal_connect (G_OBJECT (manager->priv->presence),
                      "quit_clicked",
                      G_CALLBACK (ggn_manager_menu_quit),
                      manager);
    
    /* setup the about dialog. */
    manager->priv->about = ggn_about_dialog_new ();

    /* setup the preferences window. */
    manager->priv->config = ggn_config_new ();

    /* give the config window handles to the other objects. */
    ggn_config_set_prefs (manager->priv->config,
                          manager->priv->prefs);
    ggn_config_set_presence (manager->priv->config,
                             manager->priv->presence);
    
    /* create the pointer array for the feeds. */
    manager->priv->feeds = g_ptr_array_new ();
    
    /* setup each gmail atom feed. */
    ggn_manager_update_array (manager);
    
    /* is this our first run? */
    if (ggn_preferences_get_firstrun (manager->priv->prefs)) {
        /* go ahead and show the icon. */
        /* set the notification style. */
        ggn_presence_set_notify_style (manager->priv->presence,
                                       GGN_PRESENCE_STYLE_NORMAL);
        
        /* set the notification text. */
        ggn_presence_set_notify_title (manager->priv->presence,
                                       GGN_MANAGER_FIRSTRUN_TITLE);
        ggn_presence_set_notify_summary (manager->priv->presence,
                                         GGN_MANAGER_FIRSTRUN_SUMMARY);
        
        /* show a notification. */
        ggn_presence_show_notify (manager->priv->presence);
    }
    else {
        /* refresh the atom feeds. */
        ggn_manager_refresh_feeds (manager);
    }

    /* set the feed check loop in motion. */
    gint rate = ggn_preferences_get_rate (manager->priv->prefs);
    g_timeout_add (rate * 60 * 1000, ggn_manager_iteration, manager);
}

/*
 * ggn_manager_quit:
 *
 * This function shuts the program down.
 *
 * Return value: void.
 */
void ggn_manager_quit (GgnManager* manager) {
    /* hide the dialogs. */
    ggn_about_dialog_hide (manager->priv->about);
    ggn_config_hide (manager->priv->config);
    
    /* hide the icon. */
    ggn_presence_set_icon_visible (manager->priv->presence, FALSE);
    
    /* free the objects used. */
    ggn_preferences_free (manager->priv->prefs);
    ggn_presence_free (manager->priv->presence);
    ggn_about_dialog_free (manager->priv->about);
    ggn_config_free (manager->priv->config);
    
    /* free the atom feeds. */
    g_ptr_array_free (manager->priv->feeds, TRUE);
    
    /* free the manager. */
    ggn_manager_free (manager);
    
    /* exit gtk. */
    gtk_main_quit ();
}
